#ifndef __uart_H
#define __uart_H

#include "uart.h"
#include "fifo.h"
#include "usart.h"

eTxStatus tx_status;

uint8_t txdata[TX_LEN];
uint8_t txdata_fifo[TX_LEN];
uint8_t rxdata;
uint8_t rxdata_fifo[RX_LEN];

uint16_t len = 0;

FIFO_Type fifo_tx;
FIFO_Type fifo_rx;

#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE* f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE {
  HAL_UART_Transmit(&huart2, (uint8_t*)&ch, 1, 0xFFFF);
  return ch;
}


void uart_init(void) {
  FIFO_Init(&fifo_tx, txdata_fifo, 1, TX_LEN);
  FIFO_Init(&fifo_rx, rxdata_fifo, 1, RX_LEN);

  HAL_UART_Receive_IT(&huart1, &rxdata, 1);
}

void uart_send(uint8_t* data, uint16_t len) {
  if (tx_status == USART_DMA_SENDING) {
    FIFO_Add(&fifo_tx, data, len);
  } else {
    memcpy(txdata, data, len);
    HAL_UART_Transmit_DMA(&huart1, txdata, len);
  }
  tx_status = USART_DMA_SENDING;
}

uint16_t uart_getlen(void) { return fifo_rx.Counter; }

uint16_t uart_receive(uint8_t* data, uint16_t len) {
  return FIFO_Get(&fifo_rx, data, len);
}

uint8_t uart_getbyte(uint8_t* byte) { return FIFO_GetOne(&fifo_rx, byte); }

void uart_clear() {
  FIFO_Clear(&fifo_rx);
  FIFO_Clear(&fifo_tx);
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef* huart) {
  if (huart == &huart1) {
    __HAL_DMA_DISABLE(huart->hdmatx);
    if (FIFO_IsEmpty(&fifo_tx)) {
      tx_status = USART_DMA_SENDOVER;
    } else {
      len = fifo_tx.Counter;
      FIFO_Get(&fifo_tx, txdata, len);
      HAL_UART_Transmit_DMA(&huart1, txdata, len);
    }
  }
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef* huart) {
  if (huart == &huart1) {
    FIFO_AddOne(&fifo_rx, &rxdata);
    HAL_UART_Receive_IT(&huart1, &rxdata, 1);
  }
}

#endif
